/**
 ***********************************************************************************************************************
 * Copyright (c) 2020, China Mobile Communications Group Co.,Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 
 * the License. You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 *
 * @file        opencc_api.c
 *
 * @brief       interface of character conversion module.
 *
 * @revision
 * Date         Author          Notes
 * 2020-11-20   OneOS Team      First Version
 ***********************************************************************************************************************
 */
#include <stdlib.h>
#include <string.h>
#include "opencc_general.h"
#include "opencc_mng.h"

static void cc_destroy_conv_info(opencc_info_t *info)
{
    if ((info != NULL) && (info->buf != NULL))
    {
        free(info->buf);
        info->buf = NULL;
    }

    if (info != NULL)
    {
         free(info);
         info = NULL;
    }

    return;
}

static opencc_info_t *cc_create_conv_info(int char_type, int size)
{
    opencc_info_t *output = NULL;

    output = (opencc_info_t *)calloc(1, sizeof(opencc_info_t));
    if (output == NULL)
    {
        return NULL;
    }
    
    output->buf_size = size;
    
    output->buf = (void *)calloc(1, output->buf_size);
    if (output->buf == NULL)
    {
        free(output);
        return NULL;
    }
    
    output->char_type = char_type;
    
    return output;
}

static int cc_paramer_check(opencc_info_t *src, int to_type)
{
    if (src == NULL)
    {
        return -1;
    }

    if (src->buf == NULL)
    {
        return -1;
    }

    if (src->char_type >=  CC_CHARC_TYPE_END ||
        to_type >= CC_CHARC_TYPE_END)
    {
        return -1;
    }

    return 0;
}

static void cc_conversion_handle(cc_charc_set_t *src_entry, cc_charc_set_t *dst_entry, 
                                void *src_buf, int src_size, void *dst_buf, int *pdst_size)
{
    int dst_size = 0;
    unsigned char *pDst = (unsigned char *)dst_buf;
    const unsigned char *pSrc = (const unsigned char *)src_buf;
    unsigned long code_point = 0; 
    int ret = 0;
    
    while (src_size > 0)
    {
        ret = src_entry->charc_set_to_unicode(pSrc, src_size, &code_point);
        if (code_point == 0)
        {
            break;
        }
        pSrc += ret;
        src_size -= ret;
        
        ret = dst_entry->unicode_to_charc_set(code_point, pDst, CC_INT_MAX);
        if (ret < 0)
        {
            ret = dst_entry->unicode_to_charc_set(dst_entry->unknown_char, pDst, CC_INT_MAX);
        }
        
        dst_size += ret;
        pDst += ret;
    }
    *pdst_size += dst_size;
    (void)dst_entry->unicode_to_charc_set(0, pDst, CC_INT_MAX);
    
    return;
}

/*****************************************************************************
 * 带转换字符集编码换成目标字符集编码.
 *
 * 参数:
 *    src     待转换的字符集编码地址，其中放有字符值地址、字符类型、长度。
 *    to_type 目标字符集类型。
 *
 * 返回值:
 *    返回转换后的字符集，包括字符值、长度；
 *
 * 注意:
 *     1. 本函数通过calloc分配空间来保存转换后的字符集，使用本函数成功后，
            需要释放返回值空间以及其buf所指空间。
 *        
 ****************************************************************************/
opencc_info_t *opencc_character_conversion(opencc_info_t *src, int dst_char_type)
{
    cc_charc_set_t *src_entry = NULL;
    cc_charc_set_t *dst_entry = NULL;
    int char_count = 0; 
    int byte_count = 0;
    int src_size = 0;
    int dst_size = 0;
    opencc_info_t *output = NULL;
    int ret = 0;

    /* 检查入参 */
    ret = cc_paramer_check(src, dst_char_type);
    if (ret != 0)
    {
        return NULL;
    }

    (void)cc_init();
    
    /* 待转换和目标字符集的转换对象 */
    src_entry = cc_mng_get_entry_by_type((CC_COMMON_CHARACTER_TYPE_E)src->char_type);
    dst_entry = cc_mng_get_entry_by_type((CC_COMMON_CHARACTER_TYPE_E)dst_char_type);
    if ((src_entry == NULL) || (dst_entry == NULL))
    {
        return NULL;
    }

    /* 计算待转换字符集中，待转换字符个数和其unicode值的所占字节数 */
    cc_get_char_byte_and_count(src, src_entry, &char_count, &byte_count);
    if (char_count == 0 || byte_count == 0)
    {
        return NULL;
    }
    src_size = src->buf_size;
    if (src->buf_size < byte_count)
    {
        src_size = byte_count;
    }

    /* 创建目标字符集缓存区 */
    output = cc_create_conv_info(dst_char_type, ((char_count + 1) * dst_entry->max));
    if (output == NULL)
    {
        cc_destroy_conv_info(output);
        return NULL;
    }
    
    cc_conversion_handle(src_entry, dst_entry, (void *)src->buf, src_size, (void *)output->buf, &dst_size);
    output->buf_size = dst_size;
    
    return output;
}

unsigned long cc_unicode_to_charc_set(unsigned long unicode, CC_COMMON_CHARACTER_TYPE_E dst_type)
{
    cc_charc_set_t *dst_entry = NULL;
    int ret = 0;
    unsigned long dst_value = 0;
    
    dst_entry = cc_mng_get_entry_by_type(dst_type);
    if (dst_entry == NULL)
    {
        return 0;
    }

    ret = dst_entry->unicode_to_charc_set(unicode, (void *)&dst_value, sizeof(unsigned long));
    if (ret > 0)
    {
        return dst_value;
    }
    
    return 0;
}

unsigned long cc_charc_set_to_unicode(unsigned long in, CC_COMMON_CHARACTER_TYPE_E src_type)
{
    cc_charc_set_t *src_entry = NULL;
    int ret = 0;
    unsigned long unicode = 0;
    
    src_entry = cc_mng_get_entry_by_type(src_type);
    if (src_entry == NULL)
    {
        return 0;
    }

    ret = src_entry->charc_set_to_unicode((const void *)&in, sizeof(unsigned long), &unicode);
    if (ret > 0)
    {
        return unicode;
    }
    
    return 0;
}


